package com.cavedroid;

import javax.microedition.khronos.opengles.GL10;

import android.util.Log;

public class Cave {

	public static final int TOTAL_NUMBER_OF_RINGS = 22;
	
	private static final int LOOP_DIMS = 3;
		
	private static final int LOOP_SIZE = 16;
	
	public static final float LOOP_DEPTH = 1.0f;
	
	public float initialLoopRadius = 8.0f;
	
	public float loopRadius;
	
	private static final float LOOP_SHRINK = 0.00025f;

	private static final int Z_COORD = 2;
	
	protected static final int Y_COORD = 1;

	protected static final int X_COORD = 0;
	
	private static final float LOOP_ARC = 2*(float)Math.PI/LOOP_SIZE;
	
	private static final float LOOP_ARC_DEPTH = LOOP_DEPTH/LOOP_SIZE;
	
	protected float tunnelWavelength = 17;
	
	protected float tunnelAmplitude;
	
	protected float tunnelHorizontalAmplitude;
	
	protected static final float tunnelAmpDiff = 0.6f;
	
	public float currentLoopDepth;
	
	public float[] lastCreatedSpiral;
	
	private ExtendableShape spiralVertices;
	
	private ExtendableShape[] perpendicularQueues = new ExtendableShape[LOOP_SIZE];
	
	private ExtendableShape solidTunnelWall;
	
	CyclicQueue<Float> tunnelCenter;
	
	public float[] currentSpiralPoint;
	
	public Float[] currentCenterPoint;
	
	public int spiralLoopCount;
	
	public float cameraX;
	
	public float cameraY;
	
	public float cameraZ;
	
	public float loopY;
	
	public float loopX;
	
	public float[] tunnelColor;
	
	public boolean wireframeTunnel;
	
	public Cave() {
		init();
	}
	
	public void init() {
		//Initial conditions
		cameraX = 0;
		cameraY = 0;
		cameraZ = 0;
		loopY = 0;
		loopX = 0;
		loopRadius = initialLoopRadius;
		spiralLoopCount = 0;
		spiralIterator = 0;
		currentLoopDepth = 0;
		tunnelAmplitude = 0;
		tunnelHorizontalAmplitude = 1;
		tunnelColor = new float[] {0, 1, 0};
		
		//Create tunnel buffers
		spiralVertices = new ExtendableShape(TOTAL_NUMBER_OF_RINGS, LOOP_SIZE, 1, LOOP_DIMS);
		
		solidTunnelWall = new ExtendableShape(TOTAL_NUMBER_OF_RINGS, LOOP_SIZE*2, 2, LOOP_DIMS);
		
		for (int i = 0; i < LOOP_SIZE; i++) {
			perpendicularQueues[i] = new ExtendableShape(TOTAL_NUMBER_OF_RINGS, 1, 1, LOOP_DIMS);
		}
		
		tunnelCenter = new CyclicQueue<Float>(Float.class, TOTAL_NUMBER_OF_RINGS, 3);
		
		lastCreatedSpiral = new float[LOOP_DIMS*LOOP_SIZE];
		
		currentSpiralPoint = new float[LOOP_DIMS];
		
		currentCenterPoint = new Float[3];
		
		//Generate initial tunnel
		while (spiralLoopCount <= TOTAL_NUMBER_OF_RINGS) {
        	genSpiralLoop();
        }
		
		//Standard cube drawer
		cube = new CubeDrawer();
		
		//Generate collectible
		collectible = new Collectible(cube);
		
		initObstacles();
	}
	
	protected void initObstacles() {
		obstacles = new Obstacles(cube);
	}
	
	public int spiralIterator;
	
	float ampArc = (float)Math.PI/(tunnelWavelength*LOOP_SIZE);
	
	//Calculates the amplitudes for the tunnel
	//TODO: cleanup
	protected void calculateAmplitudes() {
		tunnelAmplitude = (float)(Math.random()*2 - 1)*tunnelAmpDiff*spiralLoopCount/tunnelWavelength;
		tunnelHorizontalAmplitude = 0.8f*(float)(Math.random()*2 - 1);
	}
	
	public void genSpiralSegment() {
		
		if (spiralIterator == 0) {
			if (spiralLoopCount % tunnelWavelength == 0) {
				calculateAmplitudes();
			}
		}
		
		loopX = (float) (tunnelHorizontalAmplitude*Math.sin((spiralLoopCount*LOOP_SIZE + spiralIterator)*ampArc));
		loopY = (float) (tunnelAmplitude*Math.sin((spiralLoopCount*LOOP_SIZE + spiralIterator)*ampArc));
		
		if (spiralIterator == 0) {
			currentCenterPoint[X_COORD] = loopX;
			currentCenterPoint[Y_COORD] = loopY;
			currentCenterPoint[2] = loopRadius;
			tunnelCenter.offer(currentCenterPoint);
		}
		
		currentSpiralPoint[X_COORD] = (float) (loopX + loopRadius*Math.sin(spiralIterator*LOOP_ARC));
		currentSpiralPoint[Y_COORD] = (float) (loopY + loopRadius*Math.cos(spiralIterator*LOOP_ARC));
		currentSpiralPoint[Z_COORD] = currentLoopDepth;
		
		//Create spiral vertex
		spiralVertices.offer(currentSpiralPoint);
		
		//Create perpendicular line vertex
		perpendicularQueues[spiralIterator].offer(currentSpiralPoint);
		
		int index = spiralIterator*LOOP_DIMS;
		
		//Add to solid walls
		if (spiralLoopCount > 0) {				
			solidTunnelWall.offer(currentSpiralPoint);
			
			solidTunnelWall.offer(index, lastCreatedSpiral);
		}
		
		//Log.d("Cavedroid", tunnelEnd + " " + perpendicularQueues[spiralIterator].get(tunnelEnd)[X_COORD] + " " + currentSpiralPoint[X_COORD]);
		lastCreatedSpiral[index+X_COORD] = currentSpiralPoint[X_COORD];
		lastCreatedSpiral[index+Y_COORD] = currentSpiralPoint[Y_COORD];
		lastCreatedSpiral[index+Z_COORD] = currentSpiralPoint[Z_COORD];
		
		//Shrink tunnel
		loopRadius -= LOOP_SHRINK;
		
		currentLoopDepth -= LOOP_ARC_DEPTH;
		
		if (spiralIterator == LOOP_SIZE - 1) {
			spiralLoopCount++;
		}
		
		spiralIterator++;
		
		spiralIterator %= LOOP_SIZE;
	}
	
	public void genSpiralLoop() {
		for (int i = 0;i < LOOP_SIZE; i++) {
			genSpiralSegment();
		}
	}
	
	CubeDrawer cube;
	
	Collectible collectible;
	
	Obstacles obstacles;
	
	private void drawSpiral(GL10 gl) {
		//Draw solid wall
		if (!wireframeTunnel) {
			solidTunnelWall.drawShape(gl, GL10.GL_TRIANGLE_STRIP);
			gl.glColor4f(0, 0, 0, 1);
		}
		//Draw spiral
		spiralVertices.drawShape(gl, GL10.GL_LINE_STRIP);
		//Draw perpendiculars
		for (int i = 0; i < LOOP_SIZE; i++) {
			perpendicularQueues[i].drawShape(gl, GL10.GL_LINE_STRIP);
		}
	}
	
	public float getDistanceFromTunnel() {
		Float[] center = tunnelCenter.get(1);

		return center[2] - Math.abs(center[Y_COORD] - cameraY);
	}
	
	public float getCrashDistance() {
		float tunnelDistance = getDistanceFromTunnel();
		float obstacleDistance = obstacles.getDistance(cameraX, cameraY, cameraZ - LOOP_DEPTH);
		if (tunnelDistance > obstacleDistance) {
			tunnelDistance = obstacleDistance;
		}
		return tunnelDistance;
	}
	
	public boolean collectibleCollision() {
		if (collectible.visible) {
			float depthDistance = Math.abs(cameraZ - collectible.zPos);
			if (depthDistance < 3*Collectible.SIZE) {
				if (Math.abs(cameraY-collectible.yPos) < Collectible.SIZE) {
					return true;
				}
			}	
		}
		return false;
	}

	public void draw(GL10 gl) {
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glLoadIdentity();
		
		gl.glTranslatef(0, 0, LOOP_DEPTH);
		
		gl.glTranslatef(-cameraX, -cameraY, -cameraZ);
		
		try {
			gl.glColor4f(tunnelColor[0], tunnelColor[1], tunnelColor[2], 1);
			drawSpiral(gl);
			obstacles.drawShape(gl);
			if (collectible.visible) {
				collectible.draw(gl);
			}
		} catch (Exception e) {
			Log.e("Cavedroid", "Draw error");
		}	
		gl.glFlush();
	}
	
}
